vickcoo

iOS Developer

Leave Memory Troubles to ARC

Automatic Reference Counting, abbreviated as ARC, is a key feature of the Swift programming language. It is responsible for managing the lifecycle of objects in memory to ensure efficient allocation and deallocation of memory resources. ARC works automatically and lets you even do not manually manage memory, reducing the risks of memory leaks and improper deallocation.

  • Swift automatically manages memory and does not need manual intervention. However, you should consider the relationships between objects to prevent memory leaks.
  • Every time an instance of a class is created, ARC allocates memory to store all the data about that instance.
  • ARC automatically releases objects in memory when their lifetime ends.

How ARC Works

Its operation is simple. When an object is created, its ARC is initialized to 1. When new references point to this object, the ARC is incremented by 1. When a reference no longer exists, the ARC is decremented by 1. It's a straightforward addition and subtraction!

Let's explain this in two different ways:

Text Version

One day, David was walking down the street and saw a shiny iPhone XR. He was attracted to its features and appearance, so he decided to take it and became its owner. At this point, because David referenced the iPhone XR, the iPhone's ARC increased to 2.

After a while, John also walked by and saw the iPhone XR. He claimed that the phone belonged to him and requested it from David. Now, the iPhone's ARC became 3.

However, this situation didn't last long. Shortly after, a police officer appeared, investigated the phone's origin, and found out that the iPhone didn't belong to David or John. They were both informed that they couldn't possess the phone because it didn't belong to either of them. As a result, both David and John canceled their references to the iPhone, and the ARC quickly decreased to 1.

Finally, since no one knew who the phone belonged to, it was decided to be destroyed, and the iPhone XR also canceled its reference to the iPhone. So, the ARC became zero, and it disappeared from the world (released from memory).

Code Version

The classes Phone and Person used in the story can be found in the following code:

// One day, David was walking down the street and saw a shiny `iPhone XR`. He was attracted to its features and appearance, so he decided to take it and became its owner. At this point, because David referenced the `iPhone XR`, the iPhone's ARC increased to 2.
var iPhone: Phone? = Phone(name: "iPhone XR") // ARC = 1
var david: Person = Person(name: "David")
david.phone = iPhone // ARC = 2

// After a while, John also walked by and saw the `iPhone XR`. He claimed that the phone belonged to him and requested it from David. Now, the iPhone's ARC became 3.
var john: Person = Person(name: "John")
john.phone = iPhone // ARC = 3

// However, this situation didn't last long. Shortly after, a police officer appeared, investigated the phone's origin, and found out that the iPhone didn't belong to David or John. They were both informed that they couldn't possess the phone because it didn't belong to either of them. As a result, both David and John canceled their references to the iPhone, and the ARC quickly decreased to 1.
david.phone = nil // ARC = 2
john.phone = nil // ARC = 1

// Finally, since no one knew who the phone belonged to, it was decided to be destroyed, and the `iPhone XR` also canceled its reference to the iPhone. So, the ARC became zero, and it disappeared from the world (released from memory).
iPhone = nil // ARC = 0
class Person {
    var name: String
    var phone: Phone?
    init (name: String) {
        self.name = name
    }
}

class Phone {
    var name: String
    init(name: String) {
        self.name = name
    }
}

What is a Retain Cycle, and How Does It Happen?

A Retain Cycle refers to an object existing in memory without a way to be released. It occurs when two objects strong reference each other. The following examples explain this.

Story Version

In a distant universe, two stars existed. They attracted each other, as if destined to shine together, but their meeting was not as simple as it seemed.

My girlfriend and I loved stargazing, and one day, we found two incredibly beautiful stars. We decided to make them ours and named them Shelly and Ben. These two stars attracted each other, creating a mysterious connection, much like a romantic relationship. They established an invisible link in the universe, combining their radiance.

One day, my girlfriend and I lost interest in stargazing and decided to disconnect from those stars. However, the radiance of the stars didn't disappear. This happened because these two stars had formed a strong bond, leaving a faint line of radiance in the universe, causing them to reference each other.

Even though we no longer observed these stars and couldn't find them, their radiance continued to exist because ARC maintained their existence. They would shine forever in the universe, unable to be referenced, much like old lovers who could never meet again.

This story reflects how Automatic Reference Counting (ARC) operates. Even when the reference relationship between objects is severed, if ARC still holds references to them, they will persist, unable to be deallocated. ARC ensures correct memory management but reminds us to handle references carefully to avoid unexpected situations.

Code Version

The classes Star and Person used in the story can be found in the following code:

var me: Person = Person(name: "me")
var girlfriend: Person = Person(name: "girlfriend")
// We loved stargazing, and one day, we found two incredibly beautiful stars, and we decided to make them ours and named them `Shelly` and `Ben`
var shelly: Star = Star(name: "Shelly") // shelly ARC = 1
var ben: Star = Star(name: "Ben") // ben ARC = 1

me.star = ben
girlfriend.star = shelly

// These two stars attracted each other, creating a mysterious connection, much like a romantic relationship. They established an invisible link in the universe, combining their radiance.
ben.linkStar = shelly // ben ARC = 2
shelly.linkStar = ben // shelly ARC = 2

// One day, my girlfriend and I lost interest in stargazing and decided to disconnect from those stars.
me.star = nil // ben ARC = 1
girlfriend.star = nil // shelly ARC = 1

// However, the radiance of the stars didn't disappear. This happened because these two stars had formed a strong bond, leaving a faint line of radiance in the universe, causing them to reference each other.
// Even though we no longer observed these stars and couldn't find them, their radiance continued to exist because ARC maintained their existence. They would shine forever in the universe, unable to be referenced, much like old lovers who could never meet again.
// This story illustrates the operation of Automatic Reference Counting (ARC). Even when the reference relationships between objects have been severed, if ARC still holds references to them, they will continue to exist, unable to be deallocated. ARC ensures proper memory management but also reminds us to handle references carefully to prevent unexpected situations.
class Person {
    var name: String
    var star: Star?
    init(name: String) {
        self.name = name
    }
}

class Star {
    var name: String
    var linkStar: Star?
    init(name: String) {
        self.name = name
    }
}

How to Avoid Memory Leaks

As mentioned earlier, a Retain Cycle can lead to Memory Leaks.

  • A Retain Cycle occurs with strong references between two objects.
  • A Memory Leak happens when an object exists in memory, but the program can't access it or release it.

To avoid these issues, two keywords come to the rescue. Both methods involve preventing the object from being counted in the ARC:

The first one is weak. You can use it by adding weak in front of the variable declaration, like this:

weak var star: Star?

The second one is unowned. It is similar to weak but without the optionality. You can use it when you're sure that the object won't become nil before its lifecycle ends:

unowned var star: Star

In summary:

  • Use weak with optional variables.
  • Use unowned with non-optional variables.

Reference